#include "tools.h"
#include "boost/serialization/serialization.hpp"
#include <boost/serialization/base_object.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include "boost/algorithm/string.hpp"
#include <boost/format.hpp>
#include "Jinja2CppLight.h"

#include "fast-cpp-csv-parser/csv.h"


using google::INFO;
using google::WARNING;
using google::FATAL;
using google::ERROR;
using boost::filesystem::path;
using boost::filesystem::exists;
using Jinja2CppLight::Template;
using std::ifstream;
using std::ofstream;
using std::ios;

#ifndef BATCH_MODE
#define BATCH_MODE 0
#endif


DEFINE_string(filelist,
              "/home/d/workspace/DeepDR/data/imgs_hospital/trainfiles.csv",
              "The image list in csv format");

DEFINE_string(texfileout,
              "./tmp/tex.tex",
              "the texfile name"
);
DEFINE_string(texfilein,
              "/home/d/workspace/latexTemplate/template.tex",
              "the texfile name"
);
DEFINE_string(name,
              "张三",
              " name"
);
DEFINE_string(age,
              "45",
              "age"
);
DEFINE_string(gender,
              "男",
              "gender"
);
DEFINE_string(time,
              "2012-1-2",
              "time"
);
DEFINE_string(number,
              "345341234312",
              "number"
);
DEFINE_string(originalImg,
              "/home/d/workspace/DeepDR/data/imgs_hospital/C0011141.jpg",
              "number"
);
DEFINE_string(ODataFile,
              "data.txt",
              "data"
);
DEFINE_string(OJsonFile,
              "des.json",
              "output json file including all disease type"
);


const string templateFilePath = "/home/d/workspace/latexTemplate/template.tex";


string readFileAll(string fileName);

string calLocation(CommonImage &img) {
    Mat imgMat = img.getByte().getOriginal();
    CHECK_EQ(imgMat.depth(), CV_8U);
    CHECK_EQ(imgMat.channels(), 1);
    double r = 0, c = 0;
    double counter = 0;
    for (int i = 0; i < imgMat.rows; i++) {
        for (int j = 0; j < imgMat.cols; j++) {
            if (imgMat.at<uchar>(i, j)) {
                r += i;
                c += j;
                counter++;
            }
        }
    }
    r /= counter;
    c /= counter;
    r /= imgMat.rows;
    c /= imgMat.cols;
    string retval = "";
    if (c < 0.375) {
        retval += "视乳头";
    } else if (c < 0.625) {
        retval += "黄斑区";
    } else {
        retval += "后极部";
        return retval;
    }
    if (r > 0.5) {
        retval += "下方";
    } else {
        retval += "上方";
    }
    return retval;
}

int calNumber(CommonImage &cimg, int area = 0, int upBound = -1) {
    Mat img = cimg.getByte().getOriginal();
    Mat label, stats, centroids;
    if (upBound <= 0) {
        upBound = cimg.getOriginal().rows * cimg.getOriginal().cols;
    }
    connectedComponentsWithStats(img, label, stats, centroids, 4, CV_32S);
    int num = 0;
    for (int i = 1; i < stats.size[0]; i++) {
        if (stats.at<int32_t>(i, cv::CC_STAT_AREA) > area && stats.at<int32_t>(i, cv::CC_STAT_AREA) < upBound)
            num++;
    }
    return num;
}

int calArea(CommonImage &cimg) {
    Mat img = cimg.getByte().getOriginal();
    return (int) sum(img / 255).val[0];
}

string calStrenth(CommonImage &cimg, int thresh1 = 20, int thresh2 = 50) {
    Mat img = cimg.getByte().getOriginal();
    Mat label, stats, centroids;
    connectedComponentsWithStats(img, label, stats, centroids, 4, CV_32S);
    int area = 0;
    for (int i = 1; i < stats.size[0]; i++) {
        area += stats.at<int32_t>(i, cv::CC_STAT_AREA);
    }
    area /= stats.size[0];
    if (area < 5) {
        return "未见";
    } else if (area < thresh1) {
        return "疑似";
    } else if (area < thresh2) {
        return "散在";
    } else {
        return "出现";
    }
}

bool hasEnding(std::string const &fullString, std::string const &ending) {
    if (fullString.length() >= ending.length()) {
        return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
    } else {
        return false;
    }
}

int main(int argc, char **argv) {
    srand((unsigned int) time(NULL));
    google::SetCommandLineOption("alsologtostderr", "true");
    google::SetCommandLineOption("colorlogtostderr", "true");
    google::InitGoogleLogging(argv[0]);
    google::SetUsageMessage(
            "Convert the original image set to lmdb database\n"
    );
    google::ParseCommandLineFlags(&argc, &argv, true);
    backward::SignalHandling sh;
    if (!sh.loaded()) {
        LOG(ERROR) << "BACKWARD NOT LOADED";
    }

#if BATCH_MODE
    string fileList = FLAGS_filelist;


    string files[Parameters::NUM_LABELS + 1];
    io::CSVReader<Parameters::NUM_LABELS + 1> in(fileList);
    in.read_header(io::ignore_extra_column, "OriginalImage",
                   "Mas",
                   "Hard exclude",
                   "soft exclude"
    );
    path filePath(fileList);
    path parentPath = filePath.parent_path();
    vector<vector<string> > imagePaths;
    while (in.read_row(files[0], files[1], files[2], files[3])) {
        imagePaths.push_back(vector<string>());
        for (int i = 0; i < Parameters::NUM_LABELS + 1; i++) {
            string imageFile = (parentPath / path(files[i])).string();
            if (!boost::filesystem::exists(path(imageFile))) {
                LOG(ERROR) << "No file " << imageFile;
                continue;
            }
            imagePaths.back().push_back(imageFile);
        }
    }
#else

#endif

#if  BATCH_MODE
#pragma omp parallel for shared(imagePaths)
#endif

#if BATCH_MODE
    for (int i = 0; i < imagePaths.size(); i++) {
#endif
#if BATCH_MODE
    string tempFileName = (boost::format("tmp/%1%.tex") % i).str();
    string imgPath = imagePaths[i][0];
#else
    string tempFileName = FLAGS_texfileout;
    if (!hasEnding(tempFileName, ".tex")) {
        tempFileName += ".tex";
    }
    string imgPath = FLAGS_originalImg;
    int i = rand();
#endif
    string texfile = readFileAll(templateFilePath);

    Template temp(texfile);
    string dataTable = "";
    string tableTemplate = "%1% & %2% & %3% & %4% & %5% & %6% \\\\ \\hline \n";
    string desp = "";
    int degree = 0;

        LOG(INFO) << "handling " << imgPath << "...";
        Mat image = cv::imread(imgPath);
        cv::resize(image, image,
                   cv::Size(Parameters::IMAGE_COLS,
                            image.rows * Parameters::IMAGE_COLS / image.cols));
        DRImage drImage(image, i);
        drImage.write(i);
//        drImage.getMask().write(i);

        CommonImage vesselImg(vessel_seg(drImage.getOriginal()), "vesselImg");
        vesselImg.write(i);
    float area = calArea(vesselImg);
    float size = vesselImg.getOriginal().rows * vesselImg.getOriginal().cols;
    CHECK_GT(size, 0);
    auto ff = boost::format(tableTemplate) %
              "血管检测" %
              "-" %
              area %
              (area / size * 100) %
              "-" %
              "-";
    dataTable += ff.str();

    CommonImage bleedImg(bleed_process(drImage.getOriginal()), "bleed");
    bleedImg.write(i);
    area = calArea(bleedImg);
    ff = boost::format(tableTemplate) %
         "出血点/微血管瘤" %
         calNumber(bleedImg, 10, 500) %
         "-" %
         "-" %
         calLocation(bleedImg) %
         calStrenth(bleedImg, 5, 10);
    if (calArea(bleedImg) > 50) {
        desp += calLocation(bleedImg) + calStrenth(bleedImg, 5, 10) + "出血点/微血管瘤,";
        if (degree < 1) {
            degree = 1;
        }
    }
    dataTable += ff.str();

    ff = boost::format(tableTemplate) %
         "出血" %
         "-" %
         area %
         (area / size * 100) %
         calLocation(bleedImg) %
         calStrenth(bleedImg, 40, 80);
    dataTable += ff.str();
    if (calArea(bleedImg) > 900) {
        desp += calLocation(bleedImg) + calStrenth(bleedImg, 40, 80) + "出血";
        if (degree < 3) {
            degree = 3;
        }
    }

        CommonImage Hardleakage(leakage_preprocess(drImage.getOriginal()), "hard");
        Hardleakage.write(i);
    area = calArea(Hardleakage);
    ff = boost::format(tableTemplate) %
         "渗出" %
         calNumber(Hardleakage, 10) %
         area %
         (area / size * 100) %
         calLocation(Hardleakage) %
         calStrenth(Hardleakage, 10, 80);
    dataTable += ff.str();
    if (calArea(bleedImg) > 10) {
        desp += calLocation(bleedImg) + calStrenth(bleedImg, 10, 80) + "渗出";
        if (degree < 2) {
            degree = 2;
        }
    }

    ff = boost::format(tableTemplate) %
         "新生血管" %
         "-" %
         "-" %
         "-" %
         "-" %
         "-";
    dataTable += ff.str();
    LOG(INFO) << tableTemplate;

    string finalresult = "";
    switch (degree) {
        case 0 :
            finalresult = "未见明显DR";
            break;
        case 1:
            finalresult = "轻度NPDR";
            break;
        case 2:
            finalresult = "中度NPDR";
            break;
        case 3:
            finalresult = "重度NPDR";
            break;
        default:
            LOG(ERROR) << "NOT RECOGNIZED";
    }

    string name = FLAGS_name;
    string age = FLAGS_age;
    string time = FLAGS_time;
    string number = FLAGS_number;
    string gender = FLAGS_gender;
#if BATCH_MODE
    string originalimg = "../img/Figure_" + Common::tostring(i) + "_" + "OriginalImg.png";
#else
    string originalimg = FLAGS_originalImg;
#endif
    temp.setValue("name", name);
    temp.setValue("age", age);
    temp.setValue("time", time);
    temp.setValue("number", number);
    temp.setValue("gender", gender);
    temp.setValue("img", "{" + originalimg + "}");
    temp.setValue("datatable", dataTable);
    temp.setValue("seg1", desp);
    temp.setValue("seg2", finalresult);
    temp.setValue("seg3", "定期随访");

    string report = temp.render();
    ofstream fout(tempFileName);
    fout.write(report.c_str(), report.size());
    fout.close();
    ofstream odata(FLAGS_ODataFile);
    odata << dataTable;
    odata.close();

#if BATCH_MODE
    }
#endif

}


string readFileAll(string fileName) {
    ifstream infile(fileName);
    assert(infile.is_open());
    infile.seekg(0, ios::end);
    int fileLength = (int) infile.tellg();
    assert(fileLength > 0);
    infile.seekg(0, ios::beg);
    char *buffer = new char[fileLength];
    infile.read(buffer, fileLength);
    buffer[fileLength] = 0;
    infile.close();
    string text(buffer, (unsigned long) fileLength);
    return text;
}

